

package com.maoshi.shop.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.maoshi.shop.bean.app.dto.OrderCountData;
import com.maoshi.shop.bean.app.dto.ShopCartOrderDto;
import com.maoshi.shop.bean.app.dto.ShopCartOrderMergerDto;
import com.maoshi.shop.bean.enums.OrderStatus;
import com.maoshi.shop.bean.enums.OrderType;
import com.maoshi.shop.bean.enums.ReturnMoneyStsType;
import com.maoshi.shop.bean.event.*;
import com.maoshi.shop.bean.model.*;
import com.maoshi.shop.bean.param.GrowthParamConfig;
import com.maoshi.shop.bean.param.OrderParam;
import com.maoshi.shop.bean.param.OrderPayParam;
import com.maoshi.shop.bean.param.StatisticsRefundParam;
import com.maoshi.shop.common.exception.MaoshiShopBindException;
import com.maoshi.shop.common.util.Arith;
import com.maoshi.shop.common.util.PageAdapter;
import com.maoshi.shop.dao.OrderMapper;
import com.maoshi.shop.dao.OrderRefundMapper;
import com.maoshi.shop.dao.ProductMapper;
import com.maoshi.shop.dao.SkuMapper;
import com.maoshi.shop.service.OrderItemService;
import com.maoshi.shop.service.OrderService;
import com.maoshi.shop.service.OrderSettlementService;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lgh on 2018/09/15.
 */
@Service
@AllArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private final OrderMapper orderMapper;

    private final SkuMapper skuMapper;

    private final OrderItemService orderItemService;

    private final OrderSettlementService orderSettlementService;

    private final ProductMapper productMapper;

    private final ApplicationEventPublisher eventPublisher;

    private final OrderRefundMapper orderRefundMapper;

    @Override
    public Order getOrderByOrderNumber(String orderNumber) {
        return orderMapper.getOrderByOrderNumber(orderNumber);
    }

    @Override
    public Order getOrderByOrderNumberAndUserId(String orderNumber, String userId, boolean valid) {
        Order order = orderMapper.getOrderByOrderNumberAndUserId(orderNumber, userId);
        if (valid && order == null) {
            throw new MaoshiShopBindException("订单不存在");
        }
        return order;
    }


    @Override
    public OrderPayParam getPayUserCountByshopId(Long shopId, Date startTime, Date endTime) {
        OrderPayParam nowOrderPay = orderMapper.getOrderCountByShopId(shopId, startTime, endTime);
        StatisticsRefundParam refundParam = orderRefundMapper.getRefundByShopId(shopId, startTime, endTime);
        Double onePrice = (nowOrderPay.getPayUserCount() == 0) ? 0 : (Arith.div(nowOrderPay.getPayActualTotal(), nowOrderPay.getPayUserCount()));
        if (nowOrderPay.getPayActualTotal() == null) {
            nowOrderPay.setPayActualTotal(0.00);
        }
        nowOrderPay.setOnePrice(onePrice)
                .setRefund(refundParam.getPayActualTotal());
        return nowOrderPay;
    }

    @Override
    public OrderPayParam getActualTotalByHour(Long shopId, Date startTime, Date endTime) {
        List<OrderPayParam> actualTotalByHour = orderMapper.getActualTotalByHour(shopId, startTime, endTime);
        Map<Integer, Double> payMap = new HashMap<>(30);
        for (OrderPayParam orderPay : actualTotalByHour) {
            payMap.put(Integer.parseInt(orderPay.getDates()), orderPay.getPayActualTotal());
        }
        List<OrderPayParam> yesterdayTotalByHour = orderMapper.getActualTotalByHour(shopId, DateUtil.beginOfDay(DateUtil.yesterday()),
                DateUtil.endOfDay(DateUtil.yesterday()));
        Map<Integer, Double> yesterdayPayMap = new HashMap<>(30);
        for (OrderPayParam orderPay : yesterdayTotalByHour) {
            yesterdayPayMap.put(Integer.parseInt(orderPay.getDates()), orderPay.getPayActualTotal());
        }
        Double sum = 0.00;
        Double yesterdaySum = 0.00;
        List<Double> payYesterdayActualTotal = new ArrayList<>();
        List<Double> nowActualTotal = new ArrayList<>();
        OrderPayParam param = new OrderPayParam();
        for (int i = 0; i < 24; i++) {
            if (payMap.get(i) != null) {
                sum += payMap.get(i);
            }
            if (yesterdayPayMap.get(i) != null) {
                yesterdaySum += yesterdayPayMap.get(i);
            }
            payYesterdayActualTotal.add(Arith.round(yesterdaySum, 2));
            nowActualTotal.add(Arith.round(sum, 2));
        }
        param.setPayYesterdayActualTotal(payYesterdayActualTotal);
        param.setNowActualTotal(nowActualTotal);
        return param;
    }

    @Override
    public List<OrderPayParam> getActualTotalByDay(Long shopId, Date startTime, Date endTime) {
        List<OrderPayParam> actualTotal = orderMapper.getPayOrderByTime(shopId, startTime, endTime);
        List<OrderPayParam> actualTotalByDay = new ArrayList<>();
        //获取本月起始时间
        DateTime dateTime = DateUtil.beginOfMonth(DateUtil.date());
        Double sum = 0.00;
        for (OrderPayParam orderPayParam : actualTotal) {
            DateTime time = DateTime.of(orderPayParam.getDates(), "yyyy-MM-dd");
            if(DateUtil.compare(time,dateTime) > 0){
                sum += orderPayParam.getPayActualTotal();
                        orderPayParam.setOnePrice(Arith.round(sum,2));
            }
            actualTotalByDay.add(orderPayParam);
        }
        return actualTotalByDay;
    }


    @Override
    public Order getOrderByOrderNumberAndShopId(String orderNumber, Long shopId, boolean valid) {
        Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, orderNumber).eq(Order::getShopId, shopId));
        if (valid && order == null) {
            throw new MaoshiShopBindException("订单不存在");
        }
        return order;
    }

    @Override
    @CachePut(cacheNames = "ConfirmOrderCache", key = "#userId")
    public ShopCartOrderMergerDto putConfirmOrderCache(String userId, ShopCartOrderMergerDto shopCartOrderMergerDto) {
        return shopCartOrderMergerDto;
    }

    @Override
    @Cacheable(cacheNames = "ConfirmOrderCache", key = "#userId")
    public ShopCartOrderMergerDto getConfirmOrderCache(String userId) {
        return null;
    }

    @Override
    @CacheEvict(cacheNames = "ConfirmOrderCache", key = "#userId")
    public void removeConfirmOrderCache(String userId) {
    }



    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Order> submit(String userId, ShopCartOrderMergerDto mergerOrder) {
        List<Order> orderList = new ArrayList<>();

        /*合并代码开始*/
        List<ShopCartOrderDto> shopCartOrders = mergerOrder.getShopCartOrders();
        for (ShopCartOrderDto shopCartOrder:shopCartOrders){
            System.out.println("yoyoyoyoyoyo"+shopCartOrder.getOrderTypeBasket());
            if(shopCartOrder.getOrderTypeBasket()==3){
                mergerOrder.getUserAddr().setAddr("");
                mergerOrder.getUserAddr().setArea("");
                mergerOrder.getUserAddr().setCity("");
                mergerOrder.getUserAddr().setProvince("");
                mergerOrder.getUserAddr().setReceiver("酒店下单");
                mergerOrder.getUserAddr().setMobile("");
            }
        }
        /*合并代码结束*/

        // 提交订单
        eventPublisher.publishEvent(new SubmitOrderEvent(mergerOrder, orderList));

        /*合并代码开始*/
        for(Order o:orderList){
            o.setOrderTypeBasket(mergerOrder.getOrderTypeBasket());
        }
        /*合并代码结束*/

        // 插入订单
        orderList.forEach(order -> orderMapper.insert(order));
        List<OrderItem> orderItems = orderList.stream().flatMap(order -> order.getOrderItems().stream()).collect(Collectors.toList());
        // 插入订单项，返回主键

        /*合并代码开始*/
        for(OrderItem o:orderItems){
            o.setOrderTypeBasket(mergerOrder.getOrderTypeBasket());
        }
        /*合并代码结束*/

        orderItemService.saveBatch(orderItems);
        //计算积分情况
        if (mergerOrder.getIsScorePay() != null && mergerOrder.getIsScorePay() == 1) {
            eventPublisher.publishEvent(new SubmitScoreOrderEvent(mergerOrder, orderList));
        }
        return orderList;
    }
    public static void main(String[] args)  throws Exception{
        String json = "{'buyPrice':10,'buyOrder':1}";
        ObjectMapper om = new ObjectMapper();
        // 允许单引号（非标准）
        om.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        GrowthParamConfig growthParamConfig = om.readValue(json, GrowthParamConfig.class);
        System.out.println(growthParamConfig);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivery(Order order) {
        orderMapper.updateById(order);
        // 发送用户发货通知
        Map<String, String> params = new HashMap<>(16);
        params.put("orderNumber", order.getOrderNumber());
//		Delivery delivery = deliveryMapper.selectById(order.getDvyId());
//		params.put("dvyName", delivery.getDvyName());
//		params.put("dvyFlowId", order.getDvyFlowId());
//		smsLogService.sendSms(SmsType.NOTIFY_DVY, order.getUserId(), order.getMobile(), params);

    }

    @Override
    public List<Order> listUnRefundOrderAndOrderItems(Integer orderStatus, DateTime lessThanUpdateTime) {
        return orderMapper.listUnRefundOrderAndOrderItems(orderStatus, lessThanUpdateTime);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrders(List<Order> orders) {
        orderMapper.cancelOrders(orders);
        List<OrderItem> allOrderItems = new ArrayList<>();
        for (Order order : orders) {
            List<OrderItem> orderItems = order.getOrderItems();
            allOrderItems.addAll(orderItems);
            eventPublisher.publishEvent(new CancelOrderEvent(order));
        }
        // 如果是秒杀订单也不必还原库存
        if (CollectionUtil.isEmpty(allOrderItems) || OrderType.SECKILL.value().equals(orders.get(0).getOrderType())) {
            return;
        }
        Map<Long, Integer> prodCollect = new HashMap<>(16);
        allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getProdId)).forEach((prodId, orderItems) -> {
            int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getProdCount).sum();
            prodCollect.put(prodId, prodTotalNum);
        });
        productMapper.returnStock(prodCollect);

        Map<Long, Integer> skuCollect = new HashMap<>(16);
        allOrderItems.stream().collect(Collectors.groupingBy(OrderItem::getSkuId)).forEach((skuId, orderItems) -> {
            int prodTotalNum = orderItems.stream().mapToInt(OrderItem::getProdCount).sum();
            skuCollect.put(skuId, prodTotalNum);
        });
        skuMapper.returnStock(skuCollect);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receiptOrder(List<Order> orders) {
        orderMapper.receiptOrder(orders);
        for (Order order : orders) {
            eventPublisher.publishEvent(new ReceiptOrderEvent(order));
        }

    }

//    @Override
//    public List<Order> listOrdersDetialByOrder(Order order, Date startTime, Date endTime) {
//        return orderMapper.listOrdersDetialByOrder(order, startTime, endTime);
//    }

    @Override
    public IPage<Order> pageOrdersDetialByOrderParam(Page<Order> page, OrderParam orderParam) {
        page.setRecords(orderMapper.listOrdersDetialByOrderParam(new PageAdapter(page), orderParam));
        page.setTotal(orderMapper.countOrderDetial(orderParam));
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOrders(List<Order> orders) {
        orderMapper.deleteOrders(orders);
    }

    @Override
    public OrderCountData getOrderCount(String userId) {
        return orderMapper.getOrderCount(userId);
    }

    @Override
    public double sumTotalDistributionAmountByOrderItem(List<OrderItem> orderItems) {
        // 订单总分销金额
        double totalDistributionAmount = 0.0;
        if (CollectionUtil.isNotEmpty(orderItems)) {
            for (OrderItem orderItem : orderItems) {
                if (StrUtil.isBlank(orderItem.getDistributionCardNo())) {
                    continue;
                }
                // 如果改订单项已经退款了的话，分销员的佣金就已经回退了，不需要继续算钱
                if (Objects.equals(orderItem.getReturnMoneySts(), ReturnMoneyStsType.SUCCESS.value())) {
                    continue;
                }
                // 分销佣金
                if (orderItem.getDistributionAmount() != null && orderItem.getDistributionAmount() > 0) {
                    totalDistributionAmount = Arith.add(totalDistributionAmount, orderItem.getDistributionAmount());
                }
                // 上级分销佣金
                if (orderItem.getDistributionParentAmount() != null && orderItem.getDistributionParentAmount() > 0) {
                    totalDistributionAmount = Arith.add(totalDistributionAmount, orderItem.getDistributionParentAmount());
                }
            }
        }
        return totalDistributionAmount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeAmount(Order order) {
        Order orderDb = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getOrderId, order.getOrderId())
                .eq(Order::getOrderNumber, order.getOrderNumber())
        );
        if (!Objects.equals(orderDb.getStatus(), OrderStatus.UNPAY.value())){
            throw new MaoshiShopBindException("订单状态异常，无法更改订单金额");
        }
        //减少金额
        double amount = 0.00;
        //平台优惠金额
        double platformAmount = 0.00;
        Map<Long, OrderItem> collect = order.getOrderItems().stream().collect(Collectors.toMap(OrderItem::getOrderItemId, orderItem -> orderItem));
        List<OrderItem> orderItemList = orderItemService.list(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderNumber, order.getOrderNumber()));
        for (OrderItem orderItemDb:orderItemList){
            OrderItem orderItem = collect.get(orderItemDb.getOrderItemId());
            if (orderItem.getChageAmount() >= orderItemDb.getActualTotal() || orderItem.getChageAmount() < 0){
                throw new MaoshiShopBindException("订单金额必须大于0，或者数据发生变化(请刷新后重试)");
            }
            if (Objects.isNull(orderItem.getChageAmount()) || Objects.equals(orderItem.getChageAmount(),0.00)){
                continue;
            }
            //修改平台优惠金额
            double playformItemAmount = 0.00;
            if (orderItemDb.getPlatformShareReduce() > 0){
                //金额减少比例 = 减少金额  % 总金额
                double proportion = Arith.div(orderItem.getChageAmount(),orderItemDb.getProductTotalAmount(),6);
                //平台优惠金额减少金额 = 减少比例 * 原平台优惠金额
                playformItemAmount = Arith.round(Arith.mul(proportion,orderItemDb.getPlatformShareReduce()),2);
                //平台优惠金额 = 原平台优惠金额 - 对应比例的金额
                orderItemDb.setPlatformShareReduce(Arith.sub(orderItemDb.getPlatformShareReduce(),playformItemAmount));
            }
            //实际金额 = 原实际金额 - 变化金额
            orderItemDb.setActualTotal(Arith.sub(orderItemDb.getActualTotal(),orderItem.getChageAmount()));
            //总优惠金额 = 变化金额 + 原总优惠金额
            orderItemDb.setShareReduce(Arith.add(orderItem.getChageAmount(),orderItemDb.getShareReduce()));

            amount = Arith.add(amount,orderItem.getChageAmount());
            platformAmount = Arith.add(platformAmount,playformItemAmount);
        }
        orderItemService.updateBatchById(orderItemList);
        OrderSettlement orderSettlement = orderSettlementService.getOne(new LambdaQueryWrapper<OrderSettlement>().eq(OrderSettlement::getOrderNumber, order.getOrderNumber()));
        //修改订单信息
        orderDb.setActualTotal(Arith.sub(orderDb.getActualTotal(),amount));
        orderDb.setReduceAmount(Arith.add(orderDb.getReduceAmount(),amount));
        orderDb.setPlatformAmount(Arith.sub(orderDb.getPlatformAmount(),platformAmount));
        //初始化
        amount = 0.00;
        // 计算运费变化金额
        if(!Objects.equals(orderDb.getFreightAmount(),order.getFreightAmount())){
            amount = Arith.add(amount,Arith.sub(order.getFreightAmount(),orderDb.getFreightAmount()));
        }
        orderDb.setActualTotal(Arith.add(orderDb.getActualTotal(),amount));
        orderSettlement.setPayAmount(orderDb.getActualTotal());
        orderSettlementService.updateById(orderSettlement);
        //修改运费信息
        orderDb.setFreightAmount(order.getFreightAmount());
        orderMapper.updateById(orderDb);
    }

    @Override
    public void soldExcel(OrderParam orderParam, HttpServletResponse response) {
        List<Order> orders = orderMapper.listOrdersDetialByOrderInfo(orderParam);

        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getWriter();
        // 待发货
        String[] hearder = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付", "订单状态", "售后状态"};
        Sheet sheet = writer.getSheet();
        sheet.setColumnWidth(0, 20 * 256);
        sheet.setColumnWidth(1, 20 * 256);
        sheet.setColumnWidth(3, 20 * 256);
        sheet.setColumnWidth(4, 60 * 256);
        sheet.setColumnWidth(5, 60 * 256);

        writer.merge(hearder.length - 1, "销售信息整理");
        writer.writeRow(Arrays.asList(hearder));

        int row = 1;
        for (Order dbOrder : orders) {
            UserAddrOrder addr = dbOrder.getUserAddrOrder();
            String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr();
            List<OrderItem> orderItems = dbOrder.getOrderItems();
            int firstRow = row + 1;
            int lastRow = row + orderItems.size();
            int col = -1;
            // 订单编号
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getOrderNumber());

            // 下单时间
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getCreateTime());
            // 收件人
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, addr.getReceiver());
            // "手机"
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, addr.getMobile());
            // "收货地址"
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, addrInfo);
            int prodNameCol = ++col;
            int prodCountCol = ++col;
            for (OrderItem orderItem : orderItems) {
                row++;
                // 商品名称
                writer.writeCellValue(prodNameCol, row, orderItem.getProdName());
                // 数量
                writer.writeCellValue(prodCountCol, row, orderItem.getProdCount());
            }
            // 订单应付
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getTotal());
            // 订单运费
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getFreightAmount());
            // 订单实付
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getActualTotal());
            //订单状态
            String status = "";
            switch (dbOrder.getStatus()){
                case 1: status = "待付款"; break;
                case 2: status = "待发货"; break;
                case 3: status = "待收货"; break;
                case 4: status = "待评价"; break;
                case 5: status = "成功"; break;
                case 6: status = "失败"; break;
                case 7: status = "待成团"; break;
                default: status = "";
            }
            String refundStatus = "";
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, status);
            if (!Objects.isNull(dbOrder.getRefundStatus())){
                switch (dbOrder.getRefundStatus()){
                    case 1: refundStatus = "申请退款"; break;
                    case 2: refundStatus = "退款成功"; break;
                    case 3: refundStatus = "退款成功"; break;
                    case 4: refundStatus = "退款失败"; break;
                    default: break;
                }
            }else {
                refundStatus = "暂无售后";
            }
            mergeIfNeed(writer, firstRow, lastRow, ++col, col, refundStatus);
        }
        writeExcel(response, writer);
    }

    @Override
    public List<Order> getOrderPayInfoByOrderNumber(List<String> orderNumberList) {
        return orderMapper.getOrderPayInfoByOrderNumber(orderNumberList);
    }

    /**
     * 如果需要合并的话，就合并
     */
    private void mergeIfNeed(ExcelWriter writer, int firstRow, int lastRow, int firstColumn, int lastColumn, Object content) {
        if (lastRow - firstRow > 0 || lastColumn - firstColumn > 0) {
            writer.merge(firstRow, lastRow, firstColumn, lastColumn, content, false);
        } else {
            writer.writeCellValue(firstColumn, firstRow, content);
        }

    }
    private void writeExcel(HttpServletResponse response, ExcelWriter writer) {
        //response为HttpServletResponse对象
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        //test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
        response.setHeader("Content-Disposition", "attachment;filename=1.xls");

        ServletOutputStream servletOutputStream = null;
        try {
            servletOutputStream = response.getOutputStream();
            writer.flush(servletOutputStream);
            servletOutputStream.flush();
        } catch (IORuntimeException | IOException e) {
            e.printStackTrace();
        } finally {
            writer.close();
            try {
                if (servletOutputStream != null) {
                    servletOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
